/* Copyright 2012 Dustin Mitchell <dmmitche [at] gmail [dot] com>
 *
 * Licensed under the GPLv3.  See included LICENSE.txt for full details.
 */

#include <stdlib.h>

#include "ref_cnt.h"

int ref_cnt_create (struct ref_cnt *ref, void *obj, void (*destroy) (void *),
  void (*self_destroy) (void *))
{
  if (!ref)
    return -1;

  if (!destroy)
    destroy = free;

  if (pthread_mutex_init (&ref->sync, NULL) != 0)
    return -1;

  ref->obj = obj;
  ref->cnt = 1;
  ref->destroy = destroy;
  ref->self_destroy = self_destroy;

  return 0;
}

void ref_cnt_destroy (struct ref_cnt *ref)
{
  if (!ref)
    return;

  pthread_mutex_destroy (&ref->sync);
}

void * ref_cnt_get_obj (struct ref_cnt *ref)
{
  if (!ref)
    return NULL;

  return ref->obj;
}

int ref_cnt_ref (struct ref_cnt *ref)
{
  if (!ref)
    return -1;

  if (pthread_mutex_lock (&ref->sync) != 0)
    return -1;

  ++ref->cnt;

  pthread_mutex_unlock (&ref->sync);

  return 0;
}

int ref_cnt_deref (struct ref_cnt *ref)
{
  if (!ref)
    return -1;

  if (pthread_mutex_lock (&ref->sync) != 0)
    return -1;

  --ref->cnt;

  if (!ref->cnt) {
    pthread_mutex_unlock (&ref->sync);
    ref->destroy (ref->obj);
    ref_cnt_destroy (ref);

    if (ref->self_destroy)
      ref->self_destroy (ref);

    return 0;
  }

  pthread_mutex_unlock (&ref->sync);

  return 0;
}
